package com.wrupple.muba.common.shared;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.google.gwt.event.shared.EventBus;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.IsWidget;
import com.wrupple.muba.common.client.application.DataCallback;
import com.wrupple.muba.common.shared.event.ProcessExitEvent;
import com.wrupple.muba.widget.client.view.Task;
import com.wrupple.muba.widget.client.widgets.input.HasValueState;

/**
 * Our asynchronous finite state machine specially designed to handle States
 * that may be resolved by human interaction
 * 
 * @author japi
 * 
 * @param <I>
 * @param <O>
 */
public class SequentialProcess<I, O> extends
		OrdinalIndexedSequence<State<?, ?>> implements Process<I, O> {

	public class ProccessPipeline extends DataCallback<Object> {

		private SequenceIterator iterator;
		private StateTransition<Object> processFinishedCallback;
		private EventBus eventBus;

		public ProccessPipeline(EventBus eventBus, SequenceIterator iterator,
				StateTransition<Object> processFinishedCallback) {
			this.iterator = iterator;
			this.processFinishedCallback = processFinishedCallback;
			this.eventBus = eventBus;
		}

		@Override
		public void execute() {
			if (iterator.hasNext()) {
				// this is not the last step
				
				ProccessPipeline pipe = new ProccessPipeline(eventBus, iterator,
						processFinishedCallback);
				next(iterator, result, pipe, eventBus);
			} else {
				// this is the last step
				processFinishedCallback.setResult(result);
				
				fireDoneEvent(result,eventBus);
				processFinishedCallback.execute();
			}
		}

	}

	protected Context context;
	private Task<?, ?> currentTask;
	SequenceIterator iterator;

	private void next(Iterator<State<?, ?>> iterator, Object parameter,
			ProccessPipeline pipeline, EventBus eventBus) {
		State<I, Object> next = (State<I, Object>) iterator.next();
		if (next instanceof ContextAware) {
			((ContextAware<?, ?>) next).setContext(context);
		}
		if (next instanceof Task) {
			Task<?, ?> task = (Task<?, ?>) next;
			this.currentTask = task;
			IsWidget asWidget = task.asWidget();

			if (asWidget instanceof ContextAware) {
				((ContextAware<?, ?>) asWidget).setContext(context);
			}

			List<Task<?, ?>> userTasks = new ArrayList<Task<?, ?>>(
					size());
			for (State<?, ?> state : this) {
				if (state instanceof Task) {
					userTasks.add((Task<?, ?>) state);
				}
			}
			context.getOutputFeature().setWidget(asWidget);
		}
		next.start((I) parameter, pipeline, eventBus);
	}

	private void fireDoneEvent(Object result, EventBus eventBus) {
		eventBus.fireEvent(new ProcessExitEvent(this,result));
	}

	public void addState(State<?, ?> state) {
		assert state!=null : "adding null states to a process is not supported";
		add(state);
	}

	public com.wrupple.muba.common.shared.State.Context getContext() {
		return context;
	}

	@Override
	public void setContext(com.wrupple.muba.common.shared.State.Context context) {
		this.context = context;
	}


	public static <I, O> SequentialProcess<I, O> wrap(State<I, O> view) {
		SequentialProcess<I, O> regreso = new SequentialProcess<I, O>();
		regreso.addState(view);
		return regreso;
	}

	public static <O> SequentialProcess<O, O> wrap(HasValue<O> wrapped,
			IsWidget exactSameObjectAsWidget, String localizedName) {
		HasValueState<O> singleState = new HasValueState<O>(wrapped,
				exactSameObjectAsWidget);
		return wrap(singleState);
	}

	@Override
	public void start(I parameter, StateTransition<O> onDone, EventBus bus) {
		iterator = iterator();
		next(iterator, parameter, new ProccessPipeline(bus, iterator,
				(StateTransition<Object>) onDone), bus);

	}

	@Override
	public void addAll(Process<?, ?> process) {
		for(State<?,?> s: process){
			add(s);
		}
	}

	@Override
	public void suspend() {
		
	}
	
	@Override
	public void restore(com.wrupple.muba.common.shared.State.Context context) {
		this.context=context;
		if(currentTask!=null){
			context.getDesktopManager().restoreTask(currentTask);
		}
	}
}
